package icraf.carbon.gui;

import icraf.carbon.core.LandCover;
import icraf.carbon.core.LandCoverList;
import icraf.carbon.core.Project;
import icraf.carbon.core.Zone.DataType;
import icraf.carbon.core.ZoneList;
import icraf.carbon.gui.LCTableModel.ValueType;

import java.text.DecimalFormat;
import java.text.NumberFormat;

import javax.swing.ListModel;

public class LCRowTableModel extends MatrixTableModel {
	public enum RowType {
		TO, FROM;
	}

//	public enum ValueType {
//		TOTAL, TOTAL_NEGATIVE, TOTAL_POSITIVE, ZONE;
//	}

	private static final long serialVersionUID = 8968868974903218229L;

	private Object attributes;
	private DataType dataType;
	private boolean editable = true;
	private DecimalFormat formatter = (DecimalFormat) NumberFormat
			.getNumberInstance();
	private int initLandcoverIndex = 0;
	private boolean isAreaScale = false;
	private boolean isNegativeAllowed = true;
	private LCRowHeaderModel lcRowHeaderModel = new LCRowHeaderModel(this);
	private double multiplier = 1;
	private Project project;
	private RowType rowType = RowType.FROM;
	private ValueType valueType = ValueType.ZONE;
	private int zoneIndex = 0;

	public LCRowTableModel() {
		super();
	}

	@Override
	public Class<?> getColumnClass(int columnIndex) {
		return Double.class;
	}

	@Override
	public int getColumnCount() {
		return 1;
	}

	@Override
	public String getColumnName(int col) {
		return "Value";
	}

	public DataType getDataType() {
		return dataType;
	}

	public int getInitLandcoverIndex() {
		return initLandcoverIndex;
	}

	public Project getProject() {
		return project;
	}

	@Override
	public int getRowCount() {
		return project == null ? 0 : project.getLandCoverList().size();
	}

	@Override
	public ListModel getRowHeaderModel() {
		return lcRowHeaderModel;
	}

	@Override
	public Object getValueAt(int row, int column) {
		if (project == null || dataType == null || initLandcoverIndex<0)
			return null;

		LandCoverList l = project.getLandCoverList();
		if(initLandcoverIndex >= l.size())
			return null;
		LandCover initLandcover = l.get(initLandcoverIndex);

		LandCover lc1;
		LandCover lc2;
		if (rowType == RowType.FROM) {
			lc1 = initLandcover;
			lc2 = l.get(row);
		} else {
			lc1 = l.get(row);
			lc2 = initLandcover;
		}

		double val = 0;
		ZoneList zs = project.getZoneList();
		if (valueType == ValueType.TOTAL) {
			for (int i = 0; i < zs.getSize(); i++) {
				val += zs.get(i).getValue(lc1, lc2, dataType, attributes);
			}
		} else if (valueType == ValueType.TOTAL_POSITIVE) {
			for (int i = 0; i < zs.getSize(); i++) {
				val += Math.max(0,
						zs.get(i).getValue(lc1, lc2, dataType, attributes));
			}
		} else if (valueType == ValueType.TOTAL_NEGATIVE) {
			for (int i = 0; i < zs.getSize(); i++) {
				val += Math.min(0,
						zs.get(i).getValue(lc1, lc2, dataType, attributes));
			}
		} else {
			val = zs.get(zoneIndex).getValue(lc1, lc2, dataType, attributes);
		}
		if (Double.isNaN(val))
			return null;
		return val * multiplier;
	}

	public double getValueTotal() {
		double total = 0;
		for (int r = 0; r < getRowCount(); r++) {
			for (int c = 0; c < getColumnCount(); c++) {
				Object v = getValueAt(r, c);
				if (v != null)
					total += (Double) v;
			}
		}
		return total;
	}

	public ValueType getValueType() {
		return valueType;
	}

	public int getZoneIndex() {
		return zoneIndex;
	}

	public boolean isAreaScale() {
		return isAreaScale;
	}

	@Override
	public boolean isCellEditable(int row, int col) {
		return editable;
	}

	public boolean isNegativeAllowed() {
		return isNegativeAllowed;
	}

	public void setAreaScale(boolean isAreaScale, boolean isZoneArea) {
		this.isAreaScale = isAreaScale;
		if (isAreaScale) {
			double d = 1;
			if (isZoneArea) {
				d = project.getZoneList().get(zoneIndex).getAreaFraction()
						* project.getTotalArea();
			} else {
				d = project.getTotalArea();
			}
			multiplier = d;
		} else {
			multiplier = 1;
		}
		fireTableDataChanged();
	}

	public void setDataType(DataType dataType, Object attributes) {
		this.dataType = dataType;
		this.attributes = attributes;
		fireTableDataChanged();
	}

	public void setEditable(boolean editable) {
		this.editable = editable;
	}

	public void setLandCoverInitialIndex(int initLandcoverIndex) {
		this.initLandcoverIndex = initLandcoverIndex;
		fireTableDataChanged();
	}

	public void setNegativeAllowed(boolean isNegativeAllowed) {
		this.isNegativeAllowed = isNegativeAllowed;
	}

	public void setProject(Project z) {
		this.project = z;
		lcRowHeaderModel.setProject(z);
		fireTableStructureChanged();
	}

	public void setProject(Project z, DataType t, Object attributes) {
		setDataType(t, attributes);
		setProject(z);
	}

	public void setProject(Project project, DataType dataType,
			ValueType valueType, Object attributes) {
		setProject(project, dataType, attributes);
		setValueType(valueType);
	}

	public void setTotalFields(boolean isTotalFields) {
		fireTableStructureChanged();
	}

	@Override
	public void setValueAt(Object value, int row, int col) {
		if (valueType != ValueType.ZONE)
			return;
		if (value == null)
			return;
		if (multiplier == 0)
			return;
		double d = 0;
		if (value instanceof String) {
			try {
				d = formatter.parse((String) value).doubleValue();
			} catch (Exception e) {
				return;
			}
		} else {
			d = ((Double) value).doubleValue();
		}
		if (!isNegativeAllowed) {
			d = Math.abs(d);
		}
		LandCoverList l = project.getLandCoverList();
		LandCover initLandcover = l.get(initLandcoverIndex);
		LandCover lc1;
		LandCover lc2;
		if (rowType == RowType.FROM) {
			lc1 = initLandcover;
			lc2 = l.get(row);
		} else {
			lc1 = l.get(row);
			lc2 = initLandcover;
		}

		project.getZoneList().get(zoneIndex)
				.setValue(d / multiplier, lc1, lc2, dataType, attributes);
		fireTableCellUpdated(row, col);
	}

	public void setValueType(ValueType valueType) {
		this.valueType = valueType;
		fireTableDataChanged();
	}

	public void setZoneIndex(int zoneIndex) {
		this.zoneIndex = zoneIndex;
		fireTableDataChanged();
	}

	public RowType getRowType() {
		return rowType;
	}

	public void setRowType(RowType rowType) {
		this.rowType = rowType;
		fireTableDataChanged();
	}
}
